package org.senseframework.support.orm.mybatis.mysql;


import org.apache.commons.lang3.StringUtils;
import org.senseframework.support.orm.mybatis.definition.*;
import org.senseframework.support.orm.mybatis.query.builder.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Author lanjian
 * Email  jianlan@iflytek.com
 */
public class MysqlBaseGen {

    private static Logger logger = LoggerFactory.getLogger(MysqlBaseGen.class);

    public static Map<Class, String> typeMappings = new HashMap();

    static {
        typeMappings.put(Integer.class, "INT");
        typeMappings.put(Long.class, "BIGINT");
        typeMappings.put(Float.class, "FLOAT");
        typeMappings.put(String.class, "VARCHAR");
        typeMappings.put(Double.class, "DOUBLE");
    }

    /**
     * CREATE TABLE `user_info` (
     * `id` varchar(30) NOT NULL DEFAULT '',
     * `create_at` double DEFAULT NULL,
     * `update_at` int(11) DEFAULT NULL,
     * `poster` varchar(50) DEFAULT NULL,
     * `nickname` varchar(40) DEFAULT NULL,
     * `email` varchar(40) DEFAULT NULL,
     * `phone` varchar(100) DEFAULT NULL,
     * `password` varchar(50) DEFAULT NULL,
     * `signature` varchar(50) DEFAULT NULL,
     * `birthday` bigint(11) DEFAULT NULL,
     * `address` varchar(11) DEFAULT NULL,
     * `city` varchar(11) DEFAULT NULL,
     * `channel` varchar(11) DEFAULT NULL,
     * `invite_code` varchar(11) DEFAULT NULL,
     * `credit` int(11) DEFAULT NULL,
     * `coins` int(11) DEFAULT NULL,
     * `ip` varchar(11) DEFAULT NULL,
     * `os` varchar(11) DEFAULT NULL,
     * `client_version` varchar(11) DEFAULT NULL,
     * PRIMARY KEY (`id`)
     * ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
     *
     * @param clazz
     * @return
     */

    public static String createTable(Class clazz) {
        try {
            EntityDefinition entityDefinition = BaseQuery.resolve(clazz);
            String table = entityDefinition.getTable();
            StringBuffer stringBuffer = new StringBuffer("DROP TABLE IF EXISTS `");
            stringBuffer.append(table);
            stringBuffer.append("`; \nCREATE TABLE ");
            stringBuffer.append("`");
            stringBuffer.append(table);
            stringBuffer.append("`");
            stringBuffer.append(" ( \n");

            for (EntityColumnDefinition entityColumnDefinition : entityDefinition.getColumnDefinitions().values()) {
                stringBuffer.append("   `");
                stringBuffer.append(entityColumnDefinition.getColumn());
                stringBuffer.append("`");
                stringBuffer.append(" ");
                stringBuffer.append(typeMappings.get(entityColumnDefinition.getType()));
                if (String.class.equals(entityColumnDefinition.getType())) {
                    stringBuffer.append("(100)");
                } else {
                    stringBuffer.append("(");
                    stringBuffer.append(entityColumnDefinition.getLength());
                    stringBuffer.append(")");
                }
                stringBuffer.append(" ");
                if (entityColumnDefinition.getId()) {
                    stringBuffer.append("NOT NULL DEFAULT ''");
                } else {
                    stringBuffer.append("DEFAULT NULL");
                }
                if (StringUtils.isNotBlank(entityColumnDefinition.getDesc())) {
                    stringBuffer.append(" COMMENT ");
                    stringBuffer.append("'");
                    stringBuffer.append(entityColumnDefinition.getDesc());
                    stringBuffer.append("'");
                }
                stringBuffer.append(",\n");
            }
            stringBuffer.append("   PRIMARY KEY (`" + entityDefinition.getId() + "`)\n)ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n");
            return stringBuffer.toString();
        } catch (Exception ex) {
            if (logger.isInfoEnabled()) {
                logger.info("list", ex);
            }
            throw new RuntimeException(ex);
        }
    }


    public void orderBy(QueryBuilder queryBuilder, MYSQL sql) {
        EntityDefinition entityDefinition = queryBuilder.findEntityDefinition();
        List<Order> orders = queryBuilder.getOrders();
        if (orders == null || orders.isEmpty()) {
            return;
        }
//        Collections.reverse(orders);
        for (Order order : orders) {
            EntityColumnDefinition entityColumnDefinition = entityDefinition.getColumnDefinitions().get(order.getPropertyName());
            if (entityColumnDefinition == null) {
                throw new RuntimeException("this PropertyName " + order.getPropertyName() + " not register");
            }
            sql.ORDER_BY(entityColumnDefinition.orderBy(order));
        }
    }

    public void where(QueryBuilder queryBuilder, MYSQL sql) {
        EntityDefinition entityDefinition = queryBuilder.findEntityDefinition();
        List<Query> querySet = queryBuilder.getQueries();
        if (querySet == null || querySet.isEmpty()) {
            return;
        }
        for (Query query : querySet) {
            EntityColumnDefinition entityColumnDefinition = entityDefinition.getColumnDefinitions().get(query.getProperty());
            if (entityColumnDefinition == null) {
                throw new RuntimeException("this PropertyName " + query.getProperty() + " not register");
            }
            if (Where.kv.contains(query.getWhere())) {
                sql.WHERE(entityColumnDefinition.where(query, queryBuilder));
            }

            if (Where.single.contains(query.getWhere())) {
                sql.WHERE(entityColumnDefinition.whereSingle(query));
            }

            if (Where.array.contains(query.getWhere())) {
                sql.WHERE(entityColumnDefinition.in(query, queryBuilder));
            }

        }
    }

    public void from(QueryBuilder queryBuilder, MYSQL sql) {
        EntityDefinition entityDefinition = queryBuilder.findEntityDefinition();
        if (entityDefinition == null) {
            throw new RuntimeException("entityDefinition not register");
        }
        sql.FROM(entityDefinition.table());
        if (JoinType.inner.equals(entityDefinition.getJoinType())) {
            sql.INNER_JOIN(entityDefinition.joinTable());
            sql.ON(entityDefinition.on());
        } else if (JoinType.left.equals(entityDefinition.getJoinType())) {
            sql.LEFT_JOIN(entityDefinition.joinTable());
            sql.ON(entityDefinition.on());
        } else if (JoinType.right.equals(entityDefinition.getJoinType())) {
            sql.RIGHT_JOIN(entityDefinition.joinTable());
            sql.ON(entityDefinition.on());
        } else if (JoinType.outer.equals(entityDefinition.getJoinType())) {
            sql.OUTER_JOIN(entityDefinition.joinTable());
            sql.ON(entityDefinition.on());
        } else if (JoinType.right_outer.equals(entityDefinition.getJoinType())) {
            sql.RIGHT_OUTER_JOIN(entityDefinition.joinTable());
            sql.ON(entityDefinition.on());
        } else if (JoinType.left_outer.equals(entityDefinition.getJoinType())) {
            sql.LEFT_OUTER_JOIN(entityDefinition.joinTable());
            sql.ON(entityDefinition.on());
        }
    }

    public void column(QueryBuilder queryBuilder, MYSQL sql) {
        EntityDefinition entityDefinition = queryBuilder.findEntityDefinition();
        Collection<EntityColumnDefinition> columns = entityDefinition.getColumnDefinitions().values();
        if (columns == null || columns.isEmpty()) {
            throw new RuntimeException("columns is null");
        }
        for (EntityColumnDefinition column : columns) {
            sql.SELECT(column.column());
        }
    }


    public String list(QueryBuilder queryBuilder) {
        try {
            MYSQL sql = new MYSQL();
            column(queryBuilder, sql);
            from(queryBuilder, sql);
            where(queryBuilder, sql);
            orderBy(queryBuilder, sql);
            String sqlStr = sql.toString() + " LIMIT " + queryBuilder.begin() + "," + queryBuilder.getSize();
            if (logger.isInfoEnabled()) {
                logger.info("MysqlCURDTemple.list.sql:" + sqlStr);
            }
            return sqlStr;
        } catch (Exception ex) {
            logger.info("list", ex);
            throw new RuntimeException(ex);
        }
    }

    public String one(QueryBuilder queryBuilder) {
        try {
            MYSQL sql = new MYSQL();
            column(queryBuilder, sql);
            from(queryBuilder, sql);
            where(queryBuilder, sql);
            orderBy(queryBuilder, sql);
            String sqlStr = sql.toString() + " LIMIT 1";
            if (logger.isInfoEnabled()) {
                logger.info("MysqlCURDTemple.one.sql:" + sqlStr);
            }
            return sqlStr;
        } catch (Exception ex) {
            if (logger.isInfoEnabled()) {
                logger.info("one", ex);
            }
            throw new RuntimeException(ex);
        }
    }

    public String count(QueryBuilder queryBuilder) {
        try {
            MYSQL sql = new MYSQL();
            sql.SELECT("COUNT(1)");
            from(queryBuilder, sql);
            where(queryBuilder, sql);
            String sqlStr = sql.toString();
            if (logger.isInfoEnabled()) {
                logger.info("MysqlCURDTemple.count.sql:" + sqlStr);
            }
            return sqlStr;
        } catch (Exception ex) {
            logger.info("count", ex);
            throw new RuntimeException(ex);
        }
    }

    public String sum(QueryBuilder queryBuilder) {
        try {
            String sp = queryBuilder.getSum();
            if (StringUtils.isBlank(sp)) {
                throw new RuntimeException("sum is null");
            }
            MYSQL sql = new MYSQL();
            sql.SELECT("SUM(" + sp + ")");
            from(queryBuilder, sql);
            where(queryBuilder, sql);
            String sqlStr = sql.toString();
            if (logger.isInfoEnabled()) {
                logger.info("MysqlCURDTemple.count.sql:" + sqlStr);
            }
            return sqlStr;
        } catch (Exception ex) {
            if (logger.isInfoEnabled()) {
                logger.info("sum", ex);
            }
            throw new RuntimeException(ex);
        }
    }


    public void set(UpdateBuilder updateBuilder, MYSQL sql) {
        try {
            EntityDefinition entityDefinition = updateBuilder.findEntityDefinition();
            List<Update> updates = updateBuilder.getUpdates();
            if (updates == null || updates.isEmpty()) {
                throw new RuntimeException("no data set");
            }
            for (Update update : updates) {
                EntityColumnDefinition entityColumnDefinition = entityDefinition.getColumnDefinitions().get(update.getProperty());
                if (entityColumnDefinition == null) {
                    throw new RuntimeException("this java PropertyName " + update.getProperty() + "not register");
                }
                updateBuilder.put(update.getPropertyAs(), update.getValue());
                sql.SET(entityColumnDefinition.set(update.getPropertyAs()));
            }
        } catch (Exception ex) {
            logger.info("list", ex);
            throw new RuntimeException(ex);
        }
    }


    public void insert(InsertBuilder insertBuilder, MYSQL sql) {
        try {
            EntityDefinition entityDefinition = insertBuilder.findEntityDefinition();
            List<Update> updates = insertBuilder.getUpdates();
            if (updates == null || updates.isEmpty()) {
                throw new RuntimeException("no data set");
            }
            for (Update update : updates) {
                EntityColumnDefinition entityColumnDefinition = entityDefinition.getColumnDefinitions().get(update.getProperty());
                if (entityColumnDefinition == null) {
                    throw new RuntimeException("this java PropertyName " + update.getProperty() + " not register");
                }
                insertBuilder.put(update.getPropertyAs(), update.getValue());
                sql.VALUES(entityColumnDefinition.getColumn(), "#{" + update.getPropertyAs() + "}");
            }
        } catch (Exception ex) {
            if (logger.isInfoEnabled()) {
                logger.info("insert", ex);
            }
            throw new RuntimeException(ex);
        }
    }


    public String delete(DeleteBuilder deleteBuilder) {
        EntityDefinition entityDefinition = deleteBuilder.findEntityDefinition();
        MYSQL sql = new MYSQL();
        sql.DELETE_FROM(entityDefinition.getTable());
        where(deleteBuilder, sql);
        return sql.toString();
    }

    public String update(UpdateBuilder updateBuilder) {
        try {
            EntityDefinition entityDefinition = updateBuilder.findEntityDefinition();
            MYSQL sql = new MYSQL();
            sql.UPDATE(entityDefinition.getTable());
            set(updateBuilder, sql);
            where(updateBuilder, sql);
            String sqlStr = sql.toString();
            if (logger.isInfoEnabled()) {
                logger.info("MysqlCURDTemple.update.sql:" + sqlStr);
            }
            return sqlStr;
        } catch (Exception ex) {
            if (logger.isInfoEnabled()) {
                logger.info("update", ex);
            }
            throw new RuntimeException(ex);
        }
    }

    public String insert(InsertBuilder insertBuilder) {
        try {
            EntityDefinition entityDefinition = insertBuilder.findEntityDefinition();
            MYSQL sql = new MYSQL();
            sql.INSERT_INTO(entityDefinition.getTable());
            insert(insertBuilder, sql);
            String sqlStr = sql.toString();
            if (logger.isInfoEnabled()) {
                logger.info("MysqlCURDTemple.insert.sql:" + sqlStr);
            }
            return sqlStr;
        } catch (Exception ex) {
            if (logger.isInfoEnabled()) {
                logger.info("insert", ex);
            }
            throw new RuntimeException(ex);
        }
    }
}
